﻿using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;
using System.Drawing;

namespace Maydew.Windows.Forms.Controls.FlexiCell
{
    public partial class FlexiCellControl
    {
        #region " 私有字段 "
        private List<ColumnHeader> _fullFocusedColumns;
        private List<RowHeader> _fullFocusedRows;
        private List<Cell> _focusedCells;
        private ColumnHeader _startColumn;
        private RowHeader _startRow;
        private Cell _startCell;
        private int _splitWidth = 2;
        private bool _isResize;
        private StringAlignment _resizeType;
        private Point _mousePoint;
        #endregion

        #region " 属性 "
        public List<Cell> SelectedCells
        {
            get { return _focusedCells; }
        }
        public Cell SelectedCell
        {
            get
            {
                Cell cell = null;
                if (_focusedCells.Count > 0)
                {
                    int srow, erow;
                    string scol, ecol;
                    GetCellRectangle(_focusedCells, out srow, out scol, out erow, out ecol);
                    cell = _cells[srow, scol];
                }
                return cell;
            }
        }
        #endregion

        #region " Row "
        private void _rows_Changed(object sender, CollectionChangeEventArgs e)
        {
            RowHeader row = e.Element as RowHeader;
            switch (e.Action)
            {
                case CollectionChangeAction.Add:
                    row.MouseDown += new MouseEventHandler(row_MouseDown);
                    row.MouseMove += new MouseEventHandler(row_MouseMove);
                    row.SizeChanged += new System.EventHandler(row_SizeChanged);
                    break;
                case CollectionChangeAction.Remove:
                    row.MouseDown -= new MouseEventHandler(row_MouseDown);
                    row.MouseMove -= new MouseEventHandler(row_MouseMove);
                    row.SizeChanged -= new System.EventHandler(row_SizeChanged);
                    break;
            }
        }

        private void row_SizeChanged(object sender, System.EventArgs e)
        {
            RowHeader row = sender as RowHeader;
            List<Cell> cells = _cells.GetRow(row.RowIndex);
            SetCellsTopAndHeight(cells, row.Top, row.Height);

            int index = row.RowIndex;
            for (int r = index + 1; r < RowCount; r++)
            {
                RowHeader rowNext = _rows[r];
                rowNext.Top = _rows[r - 1].Bounds.Bottom;
                cells = _cells.GetRow(r);
                SetCellsTopAndHeight(cells, rowNext.Top, rowNext.Height);
            }

            ValidateAddRow();
            ResetScrollBar();
        }

        private void SetCellsTopAndHeight(List<Cell> cells, int top, int height)
        {
            foreach (Cell cell in cells)
            {
                cell.Top = top;
                cell.Height = height;
                if (cell.Merged && cell.MainCell.MergedCells.Count > 0)
                {
                    int srow, erow;
                    string scol, ecol;
                    GetCellRectangle(cell.MainCell.MergedCells, out srow, out scol, out erow, out ecol);
                    srow = cell.MainCell.RowIndex;
                    cell.MainCell.Height = GetRangeHeight(srow, erow);
                }
            }
        }

        private void row_MouseDown(object sender, MouseEventArgs e)
        {
            RowHeader row = sender as RowHeader;
            Rectangle rect = row.Bounds;
            rect.Inflate(0, -_splitWidth);
            if (rect.Contains(e.Location))
            {
                ClearFocusedRows();
                ClearFocusedColumns();
                ClearFocusedCells();
                row.FullRowFocused = true;
                _fullFocusedRows.Add(row);
                SetFocusedCellsByRow(row.RowIndex);
                SetFocusedColumns();
            }
            else
            {
                _mousePoint = e.Location;
                if (_mousePoint.Y < row.Top + row.Height / 2)
                {
                    _resizeType = StringAlignment.Near;
                }
                else
                {
                    _resizeType = StringAlignment.Far;
                }
                _isResize = true;

                if (_resizeType == StringAlignment.Near &&
                    row.Bounds.Top == _header.Bounds.Bottom) _isResize = false;
            }
            _mousePressed = true;
            _startRow = row;

            base.Refresh();
        }

        private void row_MouseMove(object sender, MouseEventArgs e)
        {
            RowHeader row = sender as RowHeader;
            if (_mousePressed && _startRow != null)
            {
                if (!_isResize)
                {
                    ClearFocusedRows();
                    ClearFocusedCells();
                    SetFullFocusedRows(_startRow.RowIndex, row.RowIndex);
                }
                base.Refresh();
            }
            else
            {
                Rectangle rect = row.Bounds;
                rect.Inflate(0, -_splitWidth);
                if (!rect.Contains(e.Location))
                {
                    if (e.Location.Y > row.Top + row.Height / 2 ||
                        (e.Location.Y < row.Top + row.Height / 2 &&
                        row.Bounds.Top != _header.Bounds.Bottom))
                    {
                        if (Cursor != Cursors.SizeNS) Cursor = Cursors.SizeNS;
                    }
                }
            }
        }
        #endregion

        #region " Column "
        private void _columns_Changed(object sender, System.ComponentModel.CollectionChangeEventArgs e)
        {
            ColumnHeader column = e.Element as ColumnHeader;
            switch (e.Action)
            {
                case System.ComponentModel.CollectionChangeAction.Add:
                    column.MouseDown += new MouseEventHandler(column_MouseDown);
                    column.MouseMove += new MouseEventHandler(column_MouseMove);
                    column.SizeChanged += new System.EventHandler(column_SizeChanged);
                    break;
                case System.ComponentModel.CollectionChangeAction.Remove:
                    column.MouseDown -= new MouseEventHandler(column_MouseDown);
                    column.MouseMove -= new MouseEventHandler(column_MouseMove);
                    column.SizeChanged -= new System.EventHandler(column_SizeChanged);
                    break;
            }
        }

        private void column_SizeChanged(object sender, System.EventArgs e)
        {
            ColumnHeader column = sender as ColumnHeader;
            List<Cell> cells = _cells.GetColumn(column.ColumnName);
            SetCellsLeftAndWidth(cells, column.Left, column.Width);

            int index = _columns.IndexOf(column);
            for (int c = index+1; c < ColumnCount; c++)
            {
                ColumnHeader columnNext = _columns[c];
                columnNext.Left = _columns[c-1].Bounds.Right;
                cells = _cells.GetColumn(columnNext.ColumnName);
                SetCellsLeftAndWidth(cells, columnNext.Left, columnNext.Width);
            }

            ValidateAddColumn();
            ResetScrollBar();
        }

        protected virtual void SetCellsLeftAndWidth(List<Cell> cells, int left, int width)
        {
            foreach (Cell cell in cells)
            {
                cell.Left = left;
                cell.Width = width;
                if (cell.Merged && cell.MainCell.MergedCells.Count > 0)
                {
                    int srow, erow;
                    string scol, ecol;
                    GetCellRectangle(cell.MainCell.MergedCells, out srow, out scol, out erow, out ecol);
                    scol = cell.MainCell.ColumnName;
                    cell.MainCell.Width = GetRangeWidth(_columns.IndexOf(scol), _columns.IndexOf(ecol));
                }
            }
        }

        private void column_MouseDown(object sender, MouseEventArgs e)
        {
            ColumnHeader column = sender as ColumnHeader;
            Rectangle rect = column.Bounds;
            rect.Inflate(-_splitWidth, 0);
            if (rect.Contains(e.Location))
            {
                ClearFocusedRows();
                ClearFocusedColumns();
                ClearFocusedCells();
                column.FullColumnFocused = true;
                _fullFocusedColumns.Add(column);
                SetFocusedCellsByColumn(column.ColumnName);
                SetFocusedRows();

                _isResize = false;
            }
            else
            {
                _mousePoint = e.Location;
                if (_mousePoint.X < column.Left + column.Width / 2)
                {
                    _resizeType = StringAlignment.Near;
                }
                else
                {
                    _resizeType = StringAlignment.Far;
                }
                _isResize = true;

                if (_resizeType == StringAlignment.Near && 
                    column.Bounds.Left == _header.Bounds.Right) _isResize = false;
            }

            _mousePressed = true;
            _startColumn = column;
            
            base.Refresh();
        }

        private void column_MouseMove(object sender, MouseEventArgs e)
        {
            ColumnHeader column = sender as ColumnHeader;
            if (_mousePressed && _startColumn != null)
            {
                
                if (!_isResize)
                {
                    ClearFocusedColumns();
                    ClearFocusedCells();

                    SetFullFocusedColumns(_startColumn.ColumnName, column.ColumnName);
                }
                base.Refresh();
            }
            else
            {
                Rectangle rect = column.Bounds;
                rect.Inflate(-_splitWidth, 0);
                if (!rect.Contains(e.Location))
                {
                    if(e.Location.X > column.Left + column.Width / 2 ||
                        (e.Location.X < column.Left + column.Width / 2 && 
                        column.Bounds.Left != _header.Bounds.Right))
                    {
                        if (Cursor != Cursors.SizeWE) Cursor = Cursors.SizeWE;
                    }
                }
            }
        }
        #endregion

        #region " Cell "
        private void _cells_Changed(object sender, CollectionChangeEventArgs e)
        {
            Cell cell = e.Element as Cell;
            //cell.Editor = new TextCellEditor();
            switch (e.Action)
            {
                case CollectionChangeAction.Add:
                    cell.MouseDown += new MouseEventHandler(cell_MouseDown);
                    cell.MouseMove += new MouseEventHandler(cell_MouseMove);
                    cell.MouseDoubleClick += new MouseEventHandler(cell_MouseDoubleClick);
                    //cell.Editor.KeyDown += new KeyEventHandler(Editor_KeyDown);
                    break;
                case CollectionChangeAction.Remove:
                    cell.MouseDown -= new MouseEventHandler(cell_MouseDown);
                    cell.MouseMove -= new MouseEventHandler(cell_MouseMove);
                    cell.MouseDoubleClick -= new MouseEventHandler(cell_MouseDoubleClick);
                    cell.Editor.KeyDown -= new KeyEventHandler(Editor_KeyDown);
                    break;
            }
        }

        private void Editor_KeyDown(object sender, KeyEventArgs e)
        {
            MoveCellFocused(e.KeyData);
            if ((e.KeyData & Keys.KeyCode) == Keys.Enter) this.Focus();
        }

        private void cell_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            Cell cell = sender as Cell;
            ValidateCellEditor(cell);
            cell.Editing = true;
        }

        private void cell_MouseDown(object sender, MouseEventArgs e)
        {
            ClearFocusedRows();
            ClearFocusedColumns();
            ClearFocusedCells();
            Cell cell = sender as Cell;
            //cell.Focused = true;
            //_focusedCells.Add(cell);
            SetFocusedCell(cell);
            SetFocusedRows();
            SetFocusedColumns();

            _mousePressed = true;
            _startCell = cell;

            base.Refresh();
        }

        private void cell_MouseMove(object sender, MouseEventArgs e)
        {
            if (_mousePressed && _startCell != null)
            {
                ClearFocusedRows();
                ClearFocusedColumns();
                ClearFocusedCells();
                Cell cell = sender as Cell;
                SetFocusedCells(_startCell.RowIndex, _startCell.ColumnName, cell.RowIndex, cell.ColumnName);
                SetFocusedRows();
                SetFocusedColumns();

                base.Refresh();
            }
        }
        #endregion

        #region " Set Focused "
        private void SetFocusedRows()
        {
            foreach (Cell cell in _focusedCells)
            {
                RowHeader row = _rows[cell.RowIndex];
                row.Focused = true;
                _fullFocusedRows.Add(row);
            }
        }

        private void SetFullFocusedRows(int startRowIndex, int endRowIndex)
        {
            if (startRowIndex > endRowIndex)
            {
                int temp = startRowIndex;
                startRowIndex = endRowIndex;
                endRowIndex = temp;
            }
            for (int r = startRowIndex; r <= endRowIndex; r++)
            {
                _rows[r].FullRowFocused = true;
                _fullFocusedRows.Add(_rows[r]);
                SetFocusedCellsByRow(r);
            }
        }

        private void SetFullFocusedColumns(string startColumnName, string endColumnName)
        {
            int startColumnIndex = _columns.IndexOf(_columns[startColumnName]);
            int endColumnIndex = _columns.IndexOf(_columns[endColumnName]);
            if (startColumnIndex > endColumnIndex)
            {
                int temp = startColumnIndex;
                startColumnIndex = endColumnIndex;
                endColumnIndex = temp;
            }
            for (int c = startColumnIndex; c <= endColumnIndex; c++)
            {
                _columns[c].FullColumnFocused = true;
                _fullFocusedColumns.Add(_columns[c]);
                SetFocusedCellsByColumn(_columns[c].ColumnName);
            }
        }

        private void SetFocusedColumns()
        {
            foreach (Cell cell in _focusedCells)
            {
                ColumnHeader column = _columns[cell.ColumnName];
                column.Focused = true;
                _fullFocusedColumns.Add(column);
            }
        }

        private void SetFullFocusedRows()
        {
            foreach (Cell cell in _focusedCells)
            {
                RowHeader row = _rows[cell.RowIndex];
                row.FullRowFocused = true;
                _fullFocusedRows.Add(row);
            }
        }

        private void SetFullFocusedColumns()
        {
            foreach (Cell cell in _focusedCells)
            {
                ColumnHeader column = _columns[cell.ColumnName];
                column.FullColumnFocused = true;
                _fullFocusedColumns.Add(column);
            }
        }

        private void SetFocusedCellsByRow(int rowIndex)
        {
            List<Cell> cells = _cells.GetRow(rowIndex);
            SetFocusedCells(cells);
        }

        private void SetFocusedCellsByColumn(string columnName)
        {
            List<Cell> cells = _cells.GetColumn(columnName);
            SetFocusedCells(cells);
        }

        private void SetFocusedCells(List<Cell> cells)
        {
            foreach (Cell cell in cells)
            {
                //cell.Focused = true;
                //_focusedCells.Add(cell);
                SetFocusedCell(cell);
            }
        }

        private void SetFocusedCells(int startRowIndex, string startColumnName, int endRowIndex, string endColumnName)
        {
            int startColumnIndex = _columns.IndexOf(_columns[startColumnName]);
            int endColumnIndex = _columns.IndexOf(_columns[endColumnName]);
            if (startRowIndex > endRowIndex)
            {
                int temp = startRowIndex;
                startRowIndex = endRowIndex;
                endRowIndex = temp;
            }
            if (startColumnIndex > endColumnIndex)
            {
                int temp = startColumnIndex;
                startColumnIndex = endColumnIndex;
                endColumnIndex = temp;
            }
            List<Cell> cells = new List<Cell>();
            for (int r = startRowIndex; r <= endRowIndex; r++)
            {
                for (int c = startColumnIndex; c <= endColumnIndex; c++)
                {
                    string columnName = _columns[c].ColumnName;
                    Cell cell = _cells[r, columnName];
                    cells.Add(cell);
                }
            }
            GetCellRectangle(cells, out startRowIndex, out startColumnName, out endRowIndex, out endColumnName);
            startColumnIndex = _columns.IndexOf(startColumnName);
            endColumnIndex = _columns.IndexOf(endColumnName);
            for (int r = startRowIndex; r <= endRowIndex; r++)
            {
                for (int c = startColumnIndex; c <= endColumnIndex; c++)
                {
                    string columnName = _columns[c].ColumnName;
                    Cell cell = _cells[r, columnName];
                    SetFocusedCell(cell);
                }
            }
            cells.Clear();
        }

        private void SetFocusedCell(Cell cell)
        {
            cell.Focused = true;
            if (!_focusedCells.Contains(cell))
            {
                _focusedCells.Add(cell);
            }
            if (cell.MainCell != null)
            {
                cell.MainCell.Focused = true;
                if (!_focusedCells.Contains(cell.MainCell))
                {
                    _focusedCells.Add(cell.MainCell);
                }
            }
            foreach (Cell item in cell.MergedCells)
            {
                item.Focused = true;
                if (!_focusedCells.Contains(item))
                {
                    _focusedCells.Add(item);
                }
            }
        }

        protected virtual bool MoveCellFocused(Keys keydata)
        {
            if (SelectedCell == null) return false;
            Cell cell = SelectedCell;
            int row = cell.RowIndex;
            string column = cell.ColumnName;
            bool isMove = false;
            if ((keydata & Keys.KeyCode) == Keys.Left)
            {
                int columnIndex = _columns.IndexOf(column);
                if (columnIndex - 1 >= 0)
                {
                    column = _columns[columnIndex - 1].ColumnName;
                }
                cell = _cells[row, column];
                if (cell.Merged && cell.MainCell != null) column = cell.MainCell.ColumnName;
                isMove = true;
            }
            if ((keydata & Keys.KeyCode) == Keys.Right)
            {
                if (cell.Merged)
                {
                    int srow, erow;
                    string scol, ecol;
                    GetCellRectangle(cell.MainCell.MergedCells, out srow, out scol, out erow, out ecol);
                    column = ecol;
                }
                int columnIndex = _columns.IndexOf(column);
                if (columnIndex + 1 < ColumnCount)
                {
                    column = _columns[columnIndex + 1].ColumnName;
                }
                isMove = true;
            }
            if ((keydata & Keys.KeyCode) == Keys.Up)
            {
                if (row - 1 >= 0)
                {
                    row--;
                }
                cell = _cells[row, column];
                if (cell.Merged && cell.MainCell != null) row = cell.MainCell.RowIndex;
                isMove = true;
            }
            if ((keydata & Keys.KeyCode) == Keys.Down ||
                (keydata & Keys.KeyCode) == Keys.Enter)
            {
                if (cell.Merged)
                {
                    int srow, erow;
                    string scol, ecol;
                    GetCellRectangle(cell.MainCell.MergedCells, out srow, out scol, out erow, out ecol);
                    row = erow;
                }
                if (row + 1 < RowCount)
                {
                    row++;
                }
                isMove = true;
            }
            if (isMove)
            {
                ClearFocusedRows();
                ClearFocusedColumns();
                ClearFocusedCells();
                cell = _cells[row, column];
                if (cell.Merged)
                {
                    SetFocusedCells(cell.MainCell.MergedCells);
                }
                else
                {
                    SetFocusedCell(cell);
                }
                SetFocusedRows();
                SetFocusedColumns();
                base.Refresh();
            }
            return isMove;
        }
        #endregion

        #region " Clear Focused "
        private void ClearFocusedCells()
        {
            foreach (Cell cell in _focusedCells)
            {
                cell.Focused = false;
            }
            _focusedCells.Clear();
        }

        private void ClearFocusedRows()
        {
            foreach (RowHeader item in _fullFocusedRows)
            {
                item.FullRowFocused = false;
                item.Focused = false;
            }
            _fullFocusedRows.Clear();
        }

        private void ClearFocusedColumns()
        {
            foreach (ColumnHeader item in _fullFocusedColumns)
            {
                item.FullColumnFocused = false;
                item.Focused = false;
            }
            _fullFocusedColumns.Clear();
        }
        #endregion

        #region " 友元方法--鼠标 "
        protected override void OnMouseUp(MouseEventArgs e)
        {
            _mousePressed = false;
            _startCell = null;
            _startRow = null;
            _startColumn = null;
            _isResize = false;

            base.OnMouseUp(e);
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            if (_isResize)
            {
                if (_startColumn != null)
                {
                    ColumnHeader column = _startColumn;
                    if (_resizeType == StringAlignment.Near)
                    {
                        int index = _columns.IndexOf(_startColumn);
                        if (index > 0) column = _columns[index - 1];
                    }
                    int sw = e.Location.X - _mousePoint.X;
                    int w = column.Width + sw;
                    if (w > 10) column.Width = w;
                }
                if (_startRow != null)
                {
                    RowHeader row = _startRow;
                    if (_resizeType == StringAlignment.Near)
                    {
                        int index = _rows.IndexOf(_startRow);
                        if (index > 0) row = _rows[index - 1];
                    }
                    int sh = e.Location.Y - _mousePoint.Y;
                    int h = row.Height + sh;
                    if (h > 10) row.Height = h;
                }

                _mousePoint = e.Location;
            }
            else
            {
                if (Cursor != Cursors.Default) Cursor = Cursors.Default;
            }

            base.OnMouseMove(e);
        }
        #endregion

        private void ValidateCellEditor(Cell cell)
        {
            if (cell.Editor == null)
            {
                cell.Editor.KeyDown += new KeyEventHandler(Editor_KeyDown);
            }
        }

        #region " 键盘处理 "
        /// <summary>
        /// 当键盘按下时发生。
        /// </summary>
        /// <param name="e">事件参数。</param>
        protected override void OnKeyDown(KeyEventArgs e)
        {
            Cell cell = SelectedCell;
            if (cell != null)
            {
                if (!MoveCellFocused(e.KeyData) && 
                    !e.Control && !e.Shift && !e.Alt)
                {
                    ValidateCellEditor(cell);
                    cell.Editing = true;
                    char c = (char)e.KeyCode;
                    if ((c >= '0' && c <= '9') ||
                        (c >= 'A' && c <= 'Z'))
                    {
                        SendKeys.Send(string.Format("{0}", c).ToLower());
                    }
                }
                if (e.Control && !e.Shift && !e.Alt &&
                    (e.KeyData & Keys.V) == Keys.V)
                {
                    if (Clipboard.ContainsText())
                    {
                        if (!(cell.Editor is TextCellEditor)) cell.Editor = new TextCellEditor();
                        cell.Value = Clipboard.GetText();
                    }
                    if (Clipboard.ContainsImage())
                    {
                        if (!(cell.Editor is PictureCellEditor)) cell.Editor = new PictureCellEditor();
                        cell.Value = Clipboard.GetImage();
                    }
                    base.Refresh();
                }
            }

            base.OnKeyDown(e);
        }
        /// <summary>
        /// 验证是否有效输入键。
        /// </summary>
        /// <param name="keyData">键盘参数。</param>
        /// <returns>有效输入返回true，否则返回false。</returns>
        protected override bool IsInputKey(Keys keyData)
        {
            if ((keyData & Keys.Alt) == Keys.Alt)
            {
                return false;
            }
            switch ((keyData & Keys.KeyCode))
            {
                case Keys.Left:
                case Keys.Right:
                case Keys.Up:
                case Keys.Down:
                    return true;
            }
            return base.IsInputKey(keyData);
        }
        #endregion
    }
}
